package com.sunyur.tailor.channel.service.inner.file.service.impl;


import com.sunyur.tailor.channel.common.inner.util.HttpDownloadUtil;
import com.sunyur.tailor.channel.common.middle.base.ChannelResult;
import com.sunyur.tailor.channel.common.middle.enums.FileResultEnum;
import com.sunyur.tailor.channel.common.middle.enums.MiddleResultEnum;
import com.sunyur.tailor.channel.common.middle.util.ChannelResultUtil;
import com.sunyur.tailor.channel.common.middle.util.DateUtil;
import com.sunyur.tailor.channel.common.middle.util.RedisUtil;
import com.sunyur.tailor.channel.common.middle.util.SignUtil;
import com.sunyur.tailor.channel.service.inner.file.model.FileDownLoadSecurityModel;
import com.sunyur.tailor.channel.service.inner.file.model.FileDownloadModel;
import com.sunyur.tailor.channel.service.inner.file.param.FileDownloadParam;
import com.sunyur.tailor.channel.service.inner.file.service.FileDownloadTokenLimiter;
import com.sunyur.tailor.channel.service.inner.file.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;

import java.io.File;
import java.nio.file.Files;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: HuBaoding
 * @Description:
 * @Date: Created in 10:43 2021/8/10
 */
@Slf4j
@Service("fileService")
public class FileServiceImpl implements FileService {

    /**
     * 文件存储地址
     */
    private final String TEP_DIR = "/data/download";
    /**
     * 主线文件下载地址
     */
    private final String SAAS_DOWNLOAD_FILE_URL = "/api/file/download/parts";
    @Value("${sunyur.key}")
    private String sunyurKey;
    @Value("${sunyur.secret}")
    private String sunyurSecret;
    @Value("${sunyur.domain}")
    private String sunyurDomain;
    // 24小时内 最多下载次数
    @Value("${file.download.maxsize}")
    private int fileDownloadMaxSize;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private FileDownloadTokenLimiter fileDownloadTokenLimiter;

    @Override
    public ChannelResult<FileDownloadModel> download(FileDownloadParam param) {
        // 计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("file-download-start");

        // 获取令牌，限流,无令牌直接返回
        if(!fileDownloadTokenLimiter.tryAcquire()){
            return ChannelResultUtil.error(FileResultEnum.FILE_DOWN_TASK_MAX.getCode(),
                    FileResultEnum.FILE_DOWN_TASK_MAX.getValue());
        }
        // 校验该文件下载次数，避免恶意请求
        try {
            ChannelResult<Boolean> securityChannelResult = this.security(param);
            if (!ChannelResultUtil.isSuccess(securityChannelResult)) {
                return ChannelResultUtil.error(securityChannelResult.getCode(), securityChannelResult.getMessage());
            }
        } catch (Exception e) {
            log.error("download error ", e);
            return ChannelResultUtil.error(MiddleResultEnum.FAILED.getCode(), MiddleResultEnum.FAILED.getValue());
        }
        // 校验通过后，调用主线实现下载
        ChannelResult<FileDownloadModel> channelResult= this.downloadToSunyur(param);
        stopWatch.stop();
        log.info("StopWatch file-download-start running time (millis) = {}", stopWatch.getTotalTimeMillis());
        return channelResult;
    }

    /**
     * 该方法实现：24小时之内，同一个文件不可下载超过fileDownloadMaxSize次
     *
     * @param param
     */
    public ChannelResult<Boolean> security(FileDownloadParam param) throws ParseException {
        // 获取当前时间
        Date now = new Date();

        FileDownLoadSecurityModel model = (FileDownLoadSecurityModel) redisUtil.get(param.getKey() + param.getName());
        if (model == null) {
            model = new FileDownLoadSecurityModel();
            model.setCreateTime(now);
            model.setKey(param.getKey() + param.getName());
        } else {
            // 如果创建时间和当前时间，大于1天，则重新计算
            if (DateUtil.getDateDiff(DateUtil.date2Str(model.getCreateTime(), DateUtil.DATE_FULL_STR),
                    DateUtil.date2Str(now, DateUtil.DATE_FULL_STR)) >= 1) {
                model.setCreateTime(now);
                model.setQit(1);
            } else {
                if (model.getQit() >= fileDownloadMaxSize) {
                    model.setUpdateTime(now);
                    model.setQit(model.getQit() + 1);
                    return ChannelResultUtil.error(FileResultEnum.FILE_DOWN_COUNT_MAX.getCode(),
                            FileResultEnum.FILE_DOWN_COUNT_MAX.getValue());
                }
            }
        }
        model.setUpdateTime(now);
        model.setQit(model.getQit() + 1);
        redisUtil.set(param.getKey() + param.getName(), model, 60L * 60L * 24L);
        return ChannelResultUtil.success(Boolean.TRUE);
    }

    public ChannelResult<FileDownloadModel> downloadToSunyur(FileDownloadParam param){
        ChannelResult<FileDownloadModel> channelResult = new ChannelResult<>();
        // 包装请求头信息
        Map<String, String> reqHeaders = getHeaders();
        // 判断文件临时存储地址是否存在
        File temDirFile = new File(TEP_DIR);
        if (!temDirFile.exists()) {
            temDirFile.mkdirs();
        }
        String realFileDir = null;
        try {
            // 调用主线，分片下载
            realFileDir = HttpDownloadUtil.downloadOssFileOfRangePart(sunyurDomain, reqHeaders,
                    param.getName(), param.getKey(), TEP_DIR);
            if (StringUtils.isBlank(realFileDir)) {
                channelResult.setError(FileResultEnum.FILE_DOWN_FAIL.getCode(), FileResultEnum.FILE_DOWN_FAIL.getValue());
            } else {
                byte[] readAllBytes = Files.readAllBytes(new File(realFileDir).toPath());
                channelResult.setContent(new FileDownloadModel(readAllBytes));
                channelResult.setCode(MiddleResultEnum.SUCCESS.getCode());
            }
        } catch (Exception e) {
            log.error("download error ", e);
            channelResult.setError(FileResultEnum.FILE_DOWN_FAIL.getCode(), FileResultEnum.FILE_DOWN_FAIL.getValue());
        } finally {
            // 结束时，清除已下载数据
            deleteFile(realFileDir);
        }
        return channelResult;
    }

    private Map<String, String> getHeaders() {
        Map<String, String> headers = new HashMap<>();
        headers.put("X-Ca-Key", sunyurKey);
        headers.put("orginUrl", "/api/file/download/parts");
        headers.put("Content-Type", "application/json;charset=UTF-8");
        headers.put("Accept", "application/octet-stream, application/json, application/*+json, */*");
        String sign = SignUtil.sign(sunyurSecret, "POST", SAAS_DOWNLOAD_FILE_URL, headers, null);
        headers.put("X-Ca-Signature", sign);
        return headers;
    }

    private void deleteFile(String pathName) {
        if (StringUtils.isBlank(pathName)) {
            return;
        }
        File file = new File(pathName);
        if (file.exists()) {
            file.delete();
            log.info("DownLoadFileUtil.deleteFile is delete url:{}", pathName);
        }
    }
}
